package logger

import (
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
	"log"
	"os"
	"strings"
	"time"
)

// 日志级别定义
// Level 是 zapcore.Level 的类型别名
// - zapcore.Level 是 zap 内部定义的日志等级枚举（Debug、Info、Warn、Error、Panic、Fatal）
// - 用类型别名的好处：本地代码中可以直接写 Level，不必每次写 zapcore.Level
type Level = zapcore.Level

// 定义常用日志级别常量，映射 zap 库的原始值
const (
	DebugLevel Level = zap.DebugLevel // 调试日志：开发阶段调试用
	InfoLevel  Level = zap.InfoLevel  // 普通信息：程序正常运行的关键信息
	WarnLevel  Level = zap.WarnLevel  // 警告信息：可能引发问题的情况
	ErrorLevel Level = zap.ErrorLevel // 错误信息：运行中出现的问题
	PanicLevel Level = zap.PanicLevel // Panic 级别：触发 panic
	FatalLevel Level = zap.FatalLevel // 致命错误：触发 os.Exit(1) 退出程序
)

// WithLevel 将字符串转换为日志等级枚举
// - 允许大小写混写，例如 "INFO" / "Info" / "info" 都能识别
// - 不匹配的情况默认返回 InfoLevel
func WithLevel(level string) Level {
	switch strings.ToLower(strings.TrimSpace(level)) {
	case "debug":
		return DebugLevel
	case "info":
		return InfoLevel
	case "warn", "warning":
		return WarnLevel
	case "error":
		return ErrorLevel
	case "panic":
		return PanicLevel
	case "fatal":
		return FatalLevel
	default:
		return InfoLevel
	}
}

// ==============================
// Logger 结构体定义
// ==============================

// Logger 是自定义封装的日志器
// - 内部包含一个 zap.Logger 实例
// - 保存当前日志等级（可选）
type Logger struct {
	logger *zap.Logger // zap 日志对象
	level  Level       // 当前日志等级（若不需要可删除）
}

// RotateOptions 用于配置日志文件切割策略
// - lumberjack.Logger 会根据这些选项自动轮转日志文件
type RotateOptions struct {
	FileName   string // 日志文件完整路径
	MaxSize    int    // 单个日志文件最大大小（MB）
	MaxAge     int    // 日志文件最大保留天数
	MaxBackups int    // 最多保留多少个旧日志文件
	Compress   bool   // 是否压缩归档旧日志
}

// Option 是 zap.Option 的类型别名
// - zap.Option 是 zap 提供的可选功能配置（如添加调用者信息、堆栈信息等）
type Option = zap.Option

// 常用 zap 选项的别名（简化调用）
var (
	AddCaller     = zap.AddCaller     // 在日志中记录调用位置（文件名+行号）
	AddStacktrace = zap.AddStacktrace // 达到指定等级时添加堆栈信息
	AddCallerSkip = zap.AddCallerSkip // 跳过调用栈层数（封装时避免定位到日志包装函数）
)

// ==============================
// NewLogger 日志构造函数
// ==============================
// NewLogger 创建并返回一个新的 Logger 实例
// 参数：
//   - level   : 日志等级（如 DebugLevel、InfoLevel）
//   - ropt    : 日志轮转配置
//   - options : 可选的 zap 配置（如 AddCaller、AddStacktrace）

// 功能：
//  1. 确保日志目录存在（避免写文件时出错）
//  2. 配置 zap 的日志编码器（JSON 格式，时间格式化）
//  3. 配置日志输出到终端 + 文件（支持文件轮转）
//  4. 返回一个封装好的 Logger
func NewLogger(level zapcore.Level, ropt RotateOptions, options ...Option) *Logger {
	// 配置日志编码器（时间格式）
	encCfg := zap.NewProductionEncoderConfig()
	encCfg.TimeKey = "time" // 默认为 "ts"；改成更直观的 "time"
	encCfg.EncodeTime = func(t time.Time, pae zapcore.PrimitiveArrayEncoder) {
		pae.AppendString(t.Format("2006-01-02 15:04:05"))
	}

	// 输出目标：文件（lumberjack 轮转）
	fileWriter := zapcore.AddSync(&lumberjack.Logger{
		Filename:   ropt.FileName,
		MaxSize:    ropt.MaxSize,
		MaxBackups: ropt.MaxBackups,
		MaxAge:     ropt.MaxAge,
		Compress:   ropt.Compress,
	})

	// 输出目标：控制台
	consoleWriter := zapcore.AddSync(os.Stdout)

	// 合并文件 + 控制台
	ws := zapcore.NewMultiWriteSyncer(fileWriter, consoleWriter)

	core := zapcore.NewCore(
		zapcore.NewJSONEncoder(encCfg),
		ws,
		level,
	)

	return &Logger{
		logger: zap.New(core, options...),
		level:  level,
	}
}

// Logger 返回底层 zap.Logger
func (l *Logger) Logger() *zap.Logger {
	return l.logger
}

// StdLogger 返回标准库 *log.Logger，供 http.Server.ErrorLog 使用
func (l *Logger) StdLogger() *log.Logger {
	return zap.NewStdLog(l.logger)
}

// ==============================
// 常用日志方法封装
// ==============================

// Debug 记录 Debug 级别日志
func (l *Logger) Debug(msg string, fields ...zap.Field) { l.logger.Debug(msg, fields...) }

// Info 记录 Info 级别日志
func (l *Logger) Info(msg string, fields ...zap.Field) { l.logger.Info(msg, fields...) }

// Warn 记录 Warn 级别日志
func (l *Logger) Warn(msg string, fields ...zap.Field) { l.logger.Warn(msg, fields...) }

// Error 记录 Error 级别日志
func (l *Logger) Error(msg string, fields ...zap.Field) { l.logger.Error(msg, fields...) }

// Fatal 记录 Fatal 级别日志
func (l *Logger) Fatal(msg string, fields ...zap.Field) { l.logger.Fatal(msg, fields...) }

// Sync 刷新缓冲区
// - 确保缓存的日志数据写入到文件/终端
// - 通常在程序退出前调用：defer logger.Sync()
func (l *Logger) Sync() error { return l.logger.Sync() }
